const fp = require('lodash/fp');
const fs = require('fs');

// Monad函子就是同时有静态方法of 和一个join方法的函子
// Monad 函子内部封装的值是一个函数(这个函数返回函子)，目的是通过 join 方法避免函子嵌套
class IO {
  static of(x) {
    return new IO(function () {
      return x;
    })
  }

  constructor(fn) {
    this._val = fn;
  }

  map(fn) {
    return new IO(fp.flow(this._val, fn));
  }

  join() { return this._val(); }

  flatMap(fn) { 
    return this.map(fn).join();
  }
}

const readFile = (filename) => {
  return new IO(() => {
    return fs.readFileSync(filename, 'utf-8');
  });
}

const print = (data) => { console.log(data); }

// let r = readFile('package.json').map(fp.toUpper).map(print); // 不使用扁平化 得到的是 IO(IO(x)); 
// 里面的IO是readFile返回的IO 里面的是fp.toupper得到的IO
// console.log(r._val()._val()); // 那么想得到预想的结果 就必须这样执行 显然不符合代码规范

readFile('package.json').map(fp.toUpper).flatMap(print);
// Monad函子步骤分析
// 1. readFile返回了一个 IO函子
//    这个函子的_val = () => {return fs.readFileSync(filename, 'utf-8');}
// 2. .map(fp.toUpper) 将原来的函数和toUpper合并成一个新的函数 
//    新的函数是 fp.flow(() => {return fs.readFileSync(filename, 'utf-8');}, fp.toUpper);
// 3. flatMap(print) 先是调用了map(print) 生产一个新函子 
//    这个函子的_val = fp.flow(() => {return fs.readFileSync(filename, 'utf-8');}, fp.toUpper, print);
//    然后掉用这个新函子的join方法 将上面那个函数执行 先读取到文件 然后变大写 然后打印出来。。。